﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using Cowboy.Sockets;
using GLCommon;
using Newtonsoft.Json.Linq;

namespace DeviceServer
{
    class Program
    {
        private static TcpSocketSaeaServer server;

        static void Main(string[] args)
        {

            LogTool.Init();
            LogTool.UpServerLog("DS 启动", EventType.Information);
            SimpleMessageDispatcher s = new SimpleMessageDispatcher();
            var config = new TcpSocketSaeaServerConfiguration();
            config.ReceiveTimeout = new TimeSpan(0, 0, 15);
            config.SendTimeout = new TimeSpan(0, 0, 15);
            config.FrameBuilder = new Cowboy.Sockets.LengthFieldBasedFrameBuilder(LengthField.FourBytes);
            server = new TcpSocketSaeaServer(20300, s, config);
            server.Listen();
            while (true)
            {
                string text = Console.ReadLine();
                if (text == "quit")
                    break;

            }
            LogTool.UpServerLog("DS 关闭", EventType.Information);
            server.Shutdown();

        }
    }

    public class SimpleMessageDispatcher : ITcpSocketSaeaServerMessageDispatcher
    {
        private Dictionary<string, Site> sites = new Dictionary<string, Site>();
        private Dictionary<string, List<string>> _steps = new Dictionary<string, List<string>>();
       

        public async Task OnSessionDataReceived(TcpSocketSaeaSession session, byte[] data, int offset, int count)
        {
            //var ip = session.RemoteEndPoint.Address.ToString();
            string key = session.RemoteEndPoint.GetKey();
            var text = Encoding.UTF8.GetString(data, offset, count);
            string msg = $"收到{session.RemoteEndPoint}数据：\r\n{text}";
            LogTool.UpServerLog(msg, EventType.Information);
            await  Task.Run(async () =>
            {                
                JObject jdata = null;
                bool isjson = false;
                try
                {
                    jdata = JObject.Parse(text);
                    isjson = true;
                }
                catch { }

                try
                {
                    if (isjson)
                    {
                        if (jdata["type"] != null && jdata["serial"] != null)
                        {
                            var type = jdata["type"].ToString().ToLower();
                            InitSite(jdata, session, type);
                        }
                        //AP发来 取消考试、作弊处理的命令  PC确认考生信息后，AP发来的考试格式
                        else if (jdata["command"] != null)
                        {
                            var command = (string) jdata["command"];
                            switch (command)
                            {
                                case "paper":
                                   await Send(jdata);
                                   break ;
                                case "roll":
                                    var serial = (string)jdata["serial"];
                                    var site = sites.Values.SingleOrDefault(i => i.Serail == serial);
                                    site?.Roll();
                                    break;
                                case "cancel":
                                case "cheat":
                                    break;

                            }
                        }

                        else if (jdata["km"] != null && jdata["serial"] != null)
                        {
                           await Send(jdata);
                        }

                        else if (jdata["heartbeat"] != null)
                        {
                            if (sites.ContainsKey(key))
                            {
                                var site = sites[key];
                                site.IsAlive = true;
                            }
                        }
                        //device->ds 交卷-答题内容
                        else if (jdata["submit"] != null)
                        {
                            await Submit(text, session);
                            await UpDeviceState(DeviceState.free, sites[key]);
                            ClearStep(session);
                        }
                        //答题步骤
                        else if (jdata["step"] != null) 
                        {
                            var step = (string) jdata["step"];
                            AddStep(step, session);
                        }
                    }
                }
                catch (Exception ex)
                {
                    LogTool.UpServerLog(ex.StackTrace, EventType.Error);
                }
            });

        }

        #region 处理命令

        private async Task Send(JObject jdata)
        {
            var serial = (string)jdata["serial"];
            var titleid = (string)jdata["titleid"];
            string km = (string)jdata["km"];
            int type = jdata["seattype"] != null ? (int) jdata["seattype"] : -1;
            //var type = (int) jdata["seattype"];
            Site site = null;
            if (sites.Values.Count(i => i.Serail == serial) > 1)
            {
                site = sites.Values.LastOrDefault(i => i.Serail == serial);
            }
            else
            {
                site = sites.Values.SingleOrDefault(i => i.Serail == serial);
            }


            if (site != null)
            {
                site.TitleId = titleid;
                site.km = km;
                var papercontent = site.GetPaper();

                if (site.SiteType == SiteType.meter)
                {
                    var papers = papercontent.Split('+');
                    foreach (var item in papers)
                    {
                        LogTool.UpServerLog("试卷：" + item, EventType.Information);
                        var senddata = Encoding.UTF8.GetBytes(item);
                        await site.Session.SendAsync(senddata);
                    }
                }
                else
                {
                    LogTool.UpServerLog("试卷：" + papercontent, EventType.Information);
                    var senddata = Encoding.UTF8.GetBytes(papercontent);
                    await site.Session.SendAsync(senddata);
                }
                //非低压，直接修改状态
                if (site.SiteType != SiteType.lv)
                    await UpDeviceState(DeviceState.examing, site);
            }
        }

        /// <summary>
        /// 记录答题步骤
        /// </summary>
        /// <param name="step"></param>
        /// <param name="session"></param>
        private void AddStep(string step, TcpSocketSaeaSession session)
        {
            string key = session.RemoteEndPoint.GetKey();
            if (!_steps.ContainsKey(key))
            {
                List<string> substeps = new List<string>() { step };
                _steps.Add(key, substeps);
            }
            else
            {
                _steps[key].Add(step);
            }
        }

        /// <summary>
        /// 清空答题步骤
        /// </summary>
        /// <param name="session"></param>
        private void ClearStep(TcpSocketSaeaSession session)
        {
            string key = session.RemoteEndPoint.GetKey();
            if (_steps.ContainsKey(key))
                _steps[key].Clear();
        }

        /// <summary>
        /// 初始化Site
        /// </summary>
        /// <param name="jobject"></param>
        /// <param name="session"></param>
        private void InitSite(JObject jobject, TcpSocketSaeaSession session, string t)
        {
            string ip = session.RemoteEndPoint.Address.ToString();
            var key = session.RemoteEndPoint.GetKey();
            SiteType type = (SiteType) Enum.Parse(typeof (SiteType), t);
            Site site = null;
            switch (type)
            {
                case SiteType.lv:
                    site = new LvSite();
                    break;
                case SiteType.meter:
                    site = new MeterSite();
                    break;
                case SiteType.weld:
                    site = new WeldSite();
                    break;
                default:
                    site = new Site();
                    break;
            }
            site.IP = ip;
            site.SiteType = type;
            site.Serail = (string) jobject["serial"];
            site.Version = (string) jobject["version"];
            site.Session = session;
            site.IsAlive = true;

            //var site = new Site(ip, type)
            // {
            //     Version = (string)jobject["version"],
            //     Session = session,
            //     IsAlive = true,
            //     Serail = (string)jobject["serial"]
            // };
            if (sites.Values.Count(i=>i.IP==ip)>0) 
            {
                sites[key] = site;
            }
            else
            {
                sites.Add(key, site);
            }

            DeviceConnect(jobject, site);
        }

        #endregion

        #region Session连接、断开
        public async Task OnSessionStarted(TcpSocketSaeaSession session)
        {
            LogTool.UpServerLog("OnSessionStarted  远程："+session.RemoteEndPoint , EventType.Information);
            //Console.WriteLine("OnSessionStarted  远程：{0}， session:{1}", session.RemoteEndPoint, session.SessionKey);

            //现在版本的低压板子，连接后初始化site
            //TestCurrentVision(session);
           
            //询问serial
            byte[] data = Encoding.UTF8.GetBytes("#getSerial");
            await session.Server.SendToAsync(session, data, 0, data.Length);
            await Task.CompletedTask;
        }

        private void TestCurrentVision(TcpSocketSaeaSession session)
        {
            var jdata = JObject.FromObject(new { serial="X1", version="0.0.1"});
            InitSite(jdata, session, "lv");
        }

        public async Task OnSessionClosed(TcpSocketSaeaSession session)
        {
            LogTool.UpServerLog("OnSessionClosed  远程：" + session.RemoteEndPoint, EventType.Information);
            DeviceDisConnect(session);
            await Task.CompletedTask;
        }
        #endregion

        #region 与服务器交互的方法
        /// <summary>
        /// 设备连接
        /// </summary>
        /// <param name="jreq"></param>
        /// <param name="site"></param>
        private async void DeviceConnect(JObject jreq, Site site)
        {
            if (jreq["version"] != null && jreq["serial"] != null)
            {
                string serial = (string)jreq["serial"];
                string type = (string)jreq["type"];
                var o = JObject.FromObject(
                    new
                    {
                        serial,
                        version = (string)jreq["version"],
                        type,
                    });
                try
                {
                    var result = await HttpTool.PostAsJsonAsync("devConnected", o);
                    if (result != null)
                    {
                        var jtemp = JObject.Parse(result);

                        if (jtemp["status"] != null)
                        {
                            string msg = String.Empty;
                            var status = (int) jtemp["status"];
                            switch (status)
                            {
                                case 0:
                                    msg = "已连接";
                                    break;
                                case 1:
                                    msg = "该设备不存在，请检查是否未绑定";
                                    break;
                                case -3:
                                    msg = (string) jtemp["phrase"];
                                    break;
                                default:
                                    msg = jtemp["phrase"] != null ? (string)jtemp["phrase"] : "";
                                    break;
                            }
                            LogTool.UpServerLog(site.GetSiteType()  +"设备连接："+msg, EventType.Information);
                        }
                    }
                    else
                    {
                        LogTool.UpServerLog("访问服务器失败", EventType.Information);
                    }
                }
                catch (Exception ex)
                {
                    var innerException = GetInnerException(ex);
                    LogTool.UpServerLog(innerException.Message, EventType.Error);
                }
              
            }
        }

        /// <summary>
        /// 设备断开连接
        /// </summary>
        /// <param name="session"></param>
        private async void DeviceDisConnect(TcpSocketSaeaSession session)
        {
            //string ip = session.RemoteEndPoint.Address.ToString();
            var key = session.RemoteEndPoint.GetKey();
            if (!sites.ContainsKey(key))
                return;
           
            var o = JObject.FromObject(
                new
                {
                    serial = sites[key].Serail
                });
            try
            {
                var result = await HttpTool.PostAsJsonAsync("devDisConnected", o);
                if (result != null)
                {
                    var jtemp = JObject.Parse(result);
                    if (jtemp["status"] != null)
                    {
                        int status = (int) jtemp["status"];
                        string msg = String.Empty;
                        switch (status)
                        {
                            case 0:
                                msg = "已断开";
                                break;
                            case 1:
                                msg = "该设备不存在，请检查是否未绑定";
                                break;
                            case 2:
                                msg = "已断开";
                                break;
                            case -3:
                                msg = (string) jtemp["phrase"];
                                break;
                            default:
                                msg = jtemp["phrase"] != null ? (string) jtemp["phrase"] : "";
                                break;
                        }
                        var type = sites[key].GetSiteType();
                        LogTool.UpServerLog(type + "设备断开连接：" + msg, EventType.Information);
                        sites.Remove(key);
                    }
                    else
                    {
                        LogTool.UpServerLog("访问服务器失败", EventType.Information);
                    }
                }
            }
            catch (Exception ex)
            {
                var innerException = GetInnerException(ex);
                LogTool.UpServerLog(innerException.Message, EventType.Error);
            }
            finally
            {
                if (sites.ContainsKey(key))
                    sites.Remove(key);
            }
        }

        /// <summary>
        /// 设备交卷
        /// </summary>
        /// <param name="content"></param>
        /// <param name="session"></param>
        /// <returns></returns>
        private async Task Submit(string content, TcpSocketSaeaSession session)
        {
            //string ip = session.RemoteEndPoint.Address.ToString();
            //var content = (string)jdata["submit"];
            var key = session.RemoteEndPoint.GetKey();
            var site = sites[key];
            List<string> steps = null;
            if (_steps.ContainsKey(key))
            {
                  steps = _steps[key];
            }
            try
            {
                site.Parse(content, steps);
            }
            catch (Exception ex)
            {
                LogTool.UpServerLog(ex.StackTrace, EventType.Error);
            }
            
        }

        /// <summary>
        /// 上传设备状态
        /// </summary>
        /// <param name="dstate"></param>
        /// <param name="site"></param>
        /// <returns></returns>
        private async Task UpDeviceState(DeviceState dstate, Site site)
        { 
            
            var o = JObject.FromObject(
                new
                {
                    serial= site.Serail,
                    state = dstate
                });
            try
            {
                var r = await HttpTool.PostAsJsonAsync("upstate", o);
                var temp = JObject.Parse(r);
                var status = (int) temp["status"];
                string msg = String.Empty;
                switch (status)
                {
                    case 0:
                        msg = "上传设备状态成功";
                        break;
                    case 1:
                        msg = "上传设备状态失败：考台不存在";
                        break;
                    case -3:
                        msg = "服务器内部错误，请联系管理员";
                        break;
                    default:
                        msg = temp["phrase"]!=null ?(string) temp["phrase"]:"";
                        break;
                }
                LogTool.UpServerLog(msg, EventType.Information);
            }
            catch (Exception ex)
            {
                var innerException = GetInnerException(ex);
                LogTool.UpServerLog(innerException.Message, EventType.Error);
            }
        }

        Exception GetInnerException(Exception ex)
        {
            if (ex.InnerException != null)
            {
                return GetInnerException(ex.InnerException);
            }
            return ex;
        }
        #endregion
    }

}
